//+---------------------------------------------------------------------------
//
// Copyright (C) Microsoft Corporation, 2008
//
// File: PerfCounters.cs
//
// Contents: Performance Counter singleton
//
// Created by: yunlin March 28, 2008
//
//----------------------------------------------------------------------------

using System;
using System.Diagnostics;
using System.Threading;
using System.Reflection;
using System.Collections.Generic;

namespace Microsoft.Reputation.Services
{
    public static class PerfCounters
    {
        private static string MajorVersion = Assembly.GetExecutingAssembly().GetName().Version.Major.ToString();
        private static string PerfCounterCategory = "Reputation Services";
        
        private static string ServiceNameDotVersionDot = string.Format("{0}.V{1}.", "DeliveryService", MajorVersion);

        private static string TotalRequestsCounterName = ServiceNameDotVersionDot + "RequestsTotal";
        private static string TotalLookupRequestsCounterName = ServiceNameDotVersionDot + "LookupRequestsTotal";
        private static string TotalLookupUrlRequestsCounterName = ServiceNameDotVersionDot + "LookupUrlRequestsTotal";

        private static string TotalRequestsOutstandingCounterName = ServiceNameDotVersionDot + "RequestsOutstanding";
        private static string TotalLookupRequestsOutstandingCounterName = ServiceNameDotVersionDot + "LookupRequestsOutstanding";
        private static string TotalLookupUrlRequestsOutstandingCounterName = ServiceNameDotVersionDot + "LookupUrlRequestsOutstanding";

        private static string TotalRequestsPerSecCounterName = ServiceNameDotVersionDot + "RequestsPerSec";
        private static string TotalLookupRequestsPerSecCounterName = ServiceNameDotVersionDot + "LookupRequestsPerSec";
        private static string TotalLookupUrlRequestsPerSecCounterName = ServiceNameDotVersionDot + "LookupUrlRequestsPerSec";

        // Database Requests Per Second Counters
        private static string TotalDBRequestsPerSecCounterName = ServiceNameDotVersionDot + "DBRequestsPerSec";
        private static string TotalDBLookupRequestsPerSecCounterName = ServiceNameDotVersionDot + "DBLookupRequestsPerSec";

        // Cache Counters
        private static string TotalLookupCacheHitsCounterName = ServiceNameDotVersionDot + "LookupCacheHitsTotal";
        private static string TotalLookupCacheMissesCounterName = ServiceNameDotVersionDot + "LookupCacheMissesTotal";
        private static string TotalLookupNoMatchesCachedCounterName = ServiceNameDotVersionDot + "LookupNoMatchesCachedTotal";
        private static string TotalLookupCacheHitsPerSecCounterName = ServiceNameDotVersionDot + "LookupCacheHitsPerSec";
        private static string TotalLookupCacheMissesPerSecCounterName = ServiceNameDotVersionDot + "LookupCacheMissesPerSec";

        // Overall Api Call Latency Counter
        private static string LatencyRequestAverageCounterName = ServiceNameDotVersionDot + "RequestsLatencyAverage";
        private static string LatencyRequestBaseCounterName = LatencyRequestAverageCounterName + "Base";

        // Overall Request DB Latency Counter
        private static string LatencyRequestDBAverageCounterName = ServiceNameDotVersionDot + "RequestsDBLatencyAverage";
        private static string LatencyRequestDBBaseCounterName = LatencyRequestDBAverageCounterName + "Base";

        // Lookup DB Latency Counter
        private static string LatencyLookupDBAverageCounterName = ServiceNameDotVersionDot + "LookupDBLatencyAverage";
        private static string LatencyLookupDBBaseCounterName = LatencyLookupDBAverageCounterName + "Base";

        // Lookup Api Call Latency Counter
        private static string LatencyLookupApiCallAverageCounterName = ServiceNameDotVersionDot + "LookupApiCallLatencyAverage";
        private static string LatencyLookupApiCallBaseCounterName = LatencyLookupApiCallAverageCounterName + "Base";

        // Overrides DB Latency Counter
        private static string LatencyOverridesDBAverageCounterName = ServiceNameDotVersionDot + "GetOverridesDBLatencyAverage";
        private static string LatencyOverridesDBBaseCounterName = LatencyOverridesDBAverageCounterName + "Base";

        // Lookup Api Call Latency Counter
        private static string LatencyLookupUrlApiCallAverageCounterName = ServiceNameDotVersionDot + "LookupUrlApiCallLatencyAverage";
        private static string LatencyLookupUrlApiCallBaseCounterName = LatencyLookupUrlApiCallAverageCounterName + "Base";

        // Response Type Counters
        private static string TotalKnownResponsesCounterName = ServiceNameDotVersionDot + "TotalKnownResponses";
        private static string TotalUnknownResponsesCounterName = ServiceNameDotVersionDot + "TotalUnknownResponses";

        // Lookup Api Error Counters
        private static string PercentageLookupApiFailedAverageCounterName = ServiceNameDotVersionDot + "PercentageLookupApiFailedAverage";
        private static string PercentageLookupApiFailedBaseCounterName = PercentageLookupApiFailedAverageCounterName + "Base";
        
        private static PerformanceCounter s_numberOfTotalRequests = null;
        private static PerformanceCounter s_numberOfTotalLookupRequests = null;
        private static PerformanceCounter s_numberOfTotalLookupUrlRequests = null;

        private static PerformanceCounter s_numberOfTotalRequestsOutstanding = null;
        private static PerformanceCounter s_numberOfTotalLookupRequestsOutstanding = null;
        private static PerformanceCounter s_numberOfTotalLookupUrlRequestsOutstanding = null;

        // API Reqeust Per Second Counters
        private static PerformanceCounter s_numberOfTotalRequestsPerSec = null;
        private static PerformanceCounter s_numberOfTotalLookupRequestsPerSec = null;
        private static PerformanceCounter s_numberOfTotalLookupUrlRequestsPerSec = null;

        // Database Request Per Second Counters
        private static PerformanceCounter s_numberOfTotalDBRequestsPerSec = null;
        private static PerformanceCounter s_numberOfTotalDBLookupRequestsPerSec = null;

        // Cache Lookup Counters
        private static PerformanceCounter s_numberOfTotalLookupCacheHits = null;
        private static PerformanceCounter s_numberOfTotalLookupCacheMisses = null;
        private static PerformanceCounter s_numberOfTotalLookupNoMatchesCached = null;
        private static PerformanceCounter s_numberOfTotalLookupCacheHitsPerSec = null;
        private static PerformanceCounter s_numberOfTotalLookupCacheMissesPerSec = null;

        // Query Known/Unknown Counters
        private static PerformanceCounter s_numberOfTotalKnownResponses = null;
        private static PerformanceCounter s_numberOfTotalUnknownResponses = null;

        // DB Latency Counters
        private static PerformanceCounter s_latencyRequestDBAverageTimer = null;
        private static PerformanceCounter s_latencyRequestDBAverageBase = null;

        private static PerformanceCounter s_latencyLookupDBAverageTimer = null;
        private static PerformanceCounter s_latencyLookupDBAverageBase = null;

        private static PerformanceCounter s_latencyOverridesDBAverageTimer = null;
        private static PerformanceCounter s_latencyOverridesDBAverageBase = null;

        // Request Call Latency Counters
        private static PerformanceCounter s_latencyRequestCallAverageTimer = null;
        private static PerformanceCounter s_latencyRequestCallAverageBase = null;

        // Lookup Call Latency Counters
        private static PerformanceCounter s_latencyLookupApiCallAverageTimer = null;
        private static PerformanceCounter s_latencyLookupApiCallAverageBase = null;

        // Lookup Url Call Latency Counters
        private static PerformanceCounter s_latencyLookupUrlApiCallAverageTimer = null;
        private static PerformanceCounter s_latencyLookupUrlApiCallAverageBase = null;

        // Lookup Api Error Counters
        private static PerformanceCounter s_percentageLookupApiFailedAverageCount = null;
        private static PerformanceCounter s_percentageLookupApiFailedAverageBase = null;

        private static List<PerformanceCounter> s_allCounters;

        private static bool s_perfCounterActive = false;

        public static void Initialize()
        {
            s_allCounters = new List<PerformanceCounter>();

            if (DeliveryServiceConfig.PerformanceCounterDisabled)
            {
                return;
            }

            try
            {
                if (!PerformanceCounterCategory.Exists(PerfCounterCategory))
                {
                    BaseException baseEx = BaseException.Create(
                        "Performance counters are enabled, but the category for Delivery Service does not exist."
                           + "Install the counters or disable them in web.config",
                        "Initializing performance counters for DeliveryService",
                        "PerfCounters");
                    throw baseEx;
                }

                // Overall Counters
                s_numberOfTotalRequests = CreateCounter(TotalRequestsCounterName);
                s_numberOfTotalRequestsPerSec = CreateCounter(TotalRequestsPerSecCounterName);
                s_numberOfTotalRequestsOutstanding = CreateCounter(TotalRequestsOutstandingCounterName);
                s_latencyRequestDBAverageTimer = CreateCounter(LatencyRequestDBAverageCounterName);
                s_latencyRequestDBAverageBase = CreateCounter(LatencyRequestDBBaseCounterName);
                s_numberOfTotalDBRequestsPerSec = CreateCounter(TotalDBRequestsPerSecCounterName);

                // Overall Latency Counters
                s_latencyRequestCallAverageTimer = CreateCounter(LatencyRequestAverageCounterName);
                s_latencyRequestCallAverageBase = CreateCounter(LatencyRequestBaseCounterName);

                // Lookup Counters
                s_numberOfTotalLookupRequests = CreateCounter(TotalLookupRequestsCounterName);
                s_numberOfTotalLookupRequestsPerSec = CreateCounter(TotalLookupRequestsPerSecCounterName);
                s_numberOfTotalLookupRequestsOutstanding = CreateCounter(TotalLookupRequestsOutstandingCounterName);
                s_numberOfTotalDBLookupRequestsPerSec = CreateCounter(TotalDBLookupRequestsPerSecCounterName);

                // Lookup Call Latency Counter
                s_latencyLookupApiCallAverageTimer = CreateCounter(LatencyLookupApiCallAverageCounterName);
                s_latencyLookupApiCallAverageBase = CreateCounter(LatencyLookupApiCallBaseCounterName);

                // DB Lookup Latency Counter
                s_latencyLookupDBAverageTimer = CreateCounter(LatencyLookupDBAverageCounterName);
                s_latencyLookupDBAverageBase = CreateCounter(LatencyLookupDBBaseCounterName);

                // Lookup Url Counters
                s_numberOfTotalLookupUrlRequests = CreateCounter(TotalLookupUrlRequestsCounterName);
                s_numberOfTotalLookupUrlRequestsPerSec = CreateCounter(TotalLookupUrlRequestsPerSecCounterName);
                s_numberOfTotalLookupUrlRequestsOutstanding = CreateCounter(TotalLookupUrlRequestsOutstandingCounterName);

                // Lookup Url Call Latency Counter
                s_latencyLookupUrlApiCallAverageTimer = CreateCounter(LatencyLookupUrlApiCallAverageCounterName);
                s_latencyLookupUrlApiCallAverageBase = CreateCounter(LatencyLookupUrlApiCallBaseCounterName);

                // DB Overrides Latency Counter
                s_latencyOverridesDBAverageTimer = CreateCounter(LatencyOverridesDBAverageCounterName);
                s_latencyOverridesDBAverageBase = CreateCounter(LatencyOverridesDBBaseCounterName);

                // Cache Counters
                s_numberOfTotalLookupCacheHits = CreateCounter(TotalLookupCacheHitsCounterName);
                s_numberOfTotalLookupCacheMisses = CreateCounter(TotalLookupCacheMissesCounterName);
                s_numberOfTotalLookupNoMatchesCached = CreateCounter(TotalLookupNoMatchesCachedCounterName);
                s_numberOfTotalLookupCacheHitsPerSec = CreateCounter(TotalLookupCacheHitsPerSecCounterName);
                s_numberOfTotalLookupCacheMissesPerSec = CreateCounter(TotalLookupCacheMissesPerSecCounterName);

                // Response Type Counters
                s_numberOfTotalKnownResponses = CreateCounter(TotalKnownResponsesCounterName);
                s_numberOfTotalUnknownResponses = CreateCounter(TotalUnknownResponsesCounterName);

                // Lookup Api Error Counters
                s_percentageLookupApiFailedAverageCount = CreateCounter(PercentageLookupApiFailedAverageCounterName);
                s_percentageLookupApiFailedAverageBase = CreateCounter(PercentageLookupApiFailedBaseCounterName);
                
                s_perfCounterActive = true;
            }
            catch (Exception ex)
            {
                TraceUtility.TraceCatchException(Tracing.Source, ex);
                TraceUtility.TraceThrowException(Tracing.Source, ex);
                throw;
            }
        }

        private static PerformanceCounter CreateCounter(string counterName)
        {
            PerformanceCounter perfCounter = new PerformanceCounter(
                PerfCounterCategory,
                counterName,
                false);
            s_allCounters.Add(perfCounter);

            return perfCounter;
        }

        public static void Shutdown()
        {
            s_perfCounterActive = false;

            foreach (PerformanceCounter perfCounter in s_allCounters)
            {
                perfCounter.Dispose();
            }

            // Overall Requests Counters
            s_numberOfTotalRequests = null;
            s_numberOfTotalRequestsOutstanding = null;
            s_numberOfTotalRequestsPerSec = null;
            s_latencyRequestCallAverageTimer = null;
            s_latencyRequestCallAverageBase = null;
            s_latencyRequestDBAverageTimer = null;
            s_latencyRequestDBAverageBase = null;
            s_numberOfTotalDBRequestsPerSec = null;

            // Lookup Counters            
            s_numberOfTotalLookupRequests = null;
            s_numberOfTotalLookupRequestsPerSec = null;
            s_numberOfTotalLookupRequestsOutstanding = null;
            s_latencyLookupDBAverageTimer = null;
            s_latencyLookupDBAverageBase = null;
            s_numberOfTotalDBLookupRequestsPerSec = null;
            

            // Overrides Counters
            s_latencyOverridesDBAverageTimer = null;
            s_latencyOverridesDBAverageBase = null;

            // Lookup Url Counters
            s_numberOfTotalLookupUrlRequests = null;
            s_numberOfTotalLookupUrlRequestsPerSec = null;
            s_numberOfTotalLookupUrlRequestsOutstanding = null;


            // Cache Related Counters
            s_numberOfTotalLookupCacheHits = null;
            s_numberOfTotalLookupCacheMisses = null;
            s_numberOfTotalLookupNoMatchesCached = null;
            s_numberOfTotalLookupCacheHitsPerSec = null;
            s_numberOfTotalLookupCacheMissesPerSec = null;

            // Response Type Counters
            s_numberOfTotalKnownResponses = null;
            s_numberOfTotalUnknownResponses = null;

            // Lookup Api Error Counters
            s_percentageLookupApiFailedAverageCount = null;
            s_percentageLookupApiFailedAverageBase = null;
        }

        public static void LookupObjectsApiCalled()
        {
            try
            {
                if (s_perfCounterActive)
                {
                    s_numberOfTotalRequests.Increment();
                    s_numberOfTotalRequestsOutstanding.Increment();
                    s_numberOfTotalLookupRequests.Increment();
                    s_numberOfTotalLookupRequestsOutstanding.Increment();
                }
            }
            catch (Exception ex)
            {
                if (DeliveryServiceHelpers.IsFatalException(ex))
                {
                    throw;
                }

                TraceUtility.TraceCatchException(Tracing.Source, ex);
                s_perfCounterActive = false;
            }
        }

        public static void LookupCategoriesForUrlApiCalled()
        {
            try
            {
                if (s_perfCounterActive)
                {
                    s_numberOfTotalRequests.Increment();
                    s_numberOfTotalRequestsOutstanding.Increment();
                    s_numberOfTotalLookupUrlRequests.Increment();
                    s_numberOfTotalLookupUrlRequestsOutstanding.Increment();
                }
            }
            catch (Exception ex)
            {
                if (DeliveryServiceHelpers.IsFatalException(ex))
                {
                    throw;
                }

                TraceUtility.TraceCatchException(Tracing.Source, ex);
                s_perfCounterActive = false;
            }
        }

        public static void LookupObjectsApiCompleted(long ticks, uint operStatus)
        {
            try
            {
                if (s_perfCounterActive)
                {
                    s_numberOfTotalLookupRequestsOutstanding.Decrement();
                    s_numberOfTotalRequestsOutstanding.Decrement();

                    s_numberOfTotalLookupRequestsPerSec.Increment();
                    s_numberOfTotalRequestsPerSec.Increment();

                    s_latencyLookupApiCallAverageTimer.IncrementBy(ticks);
                    s_latencyLookupApiCallAverageBase.Increment();

                    // Bump up the overall counter for all request types
                    s_latencyRequestCallAverageTimer.IncrementBy(ticks);
                    s_latencyRequestCallAverageBase.Increment();

                    if (operStatus == StatusCodes.Error)
                    {
                        s_percentageLookupApiFailedAverageCount.Increment();
                    }
                    s_percentageLookupApiFailedAverageBase.Increment();
                }
            }
            catch (Exception ex)
            {
                if (DeliveryServiceHelpers.IsFatalException(ex))
                {
                    throw;
                }

                TraceUtility.TraceCatchException(Tracing.Source, ex);
                s_perfCounterActive = false;
            }
        }

        public static void LookupCategoriesForUrlApiCompleted(long ticks)
        {
            try
            {
                if (s_perfCounterActive)
                {
                    s_numberOfTotalLookupUrlRequestsOutstanding.Decrement();
                    s_numberOfTotalRequestsOutstanding.Decrement();

                    s_numberOfTotalLookupUrlRequestsPerSec.Increment();
                    s_numberOfTotalRequestsPerSec.Increment();

                    s_latencyLookupUrlApiCallAverageTimer.IncrementBy(ticks);
                    s_latencyLookupUrlApiCallAverageBase.Increment();

                    // Bump up the overall counter for all request types
                    s_latencyRequestCallAverageTimer.IncrementBy(ticks);
                    s_latencyRequestCallAverageBase.Increment();
                }
            }
            catch (Exception ex)
            {
                if (DeliveryServiceHelpers.IsFatalException(ex))
                {
                    throw;
                }

                TraceUtility.TraceCatchException(Tracing.Source, ex);
                s_perfCounterActive = false;
            }
        }

        public static void LookupObjectCacheHit()
        {
            try
            {
                if (s_perfCounterActive)
                {
                    s_numberOfTotalLookupCacheHits.Increment();
                    s_numberOfTotalLookupCacheHitsPerSec.Increment();
                }
            }
            catch (Exception ex)
            {
                if (DeliveryServiceHelpers.IsFatalException(ex))
                {
                    throw;
                }

                TraceUtility.TraceCatchException(Tracing.Source, ex);
                s_perfCounterActive = false;
            }
        }

        public static void LookupObjectCacheMiss()
        {
            try
            {
                if (s_perfCounterActive)
                {
                    s_numberOfTotalLookupCacheMisses.Increment();
                    s_numberOfTotalLookupCacheMissesPerSec.Increment();
                }
            }
            catch (Exception ex)
            {
                if (DeliveryServiceHelpers.IsFatalException(ex))
                {
                    throw;
                }

                TraceUtility.TraceCatchException(Tracing.Source, ex);
                s_perfCounterActive = false;
            }
        }

        public static void LookupNoMatchObjectCached()
        {
            try
            {
                if (s_perfCounterActive)
                {
                    s_numberOfTotalLookupNoMatchesCached.Increment();
                }
            }
            catch (Exception ex)
            {
                if (DeliveryServiceHelpers.IsFatalException(ex))
                {
                    throw;
                }

                TraceUtility.TraceCatchException(Tracing.Source, ex);
                s_perfCounterActive = false;
            }
        }

        #region DB Latency Counters
        public static void LookupDBLatency(long ticks)
        {
            try
            {
                if (s_perfCounterActive)
                {
                    s_latencyLookupDBAverageTimer.IncrementBy(ticks);
                    s_latencyLookupDBAverageBase.Increment();
                    s_numberOfTotalDBLookupRequestsPerSec.Increment();

                    // Bump up the counter for all DB Requests
                    s_latencyRequestDBAverageTimer.IncrementBy(ticks);
                    s_latencyRequestDBAverageBase.Increment();
                    s_numberOfTotalDBRequestsPerSec.Increment();
                }
            }
            catch (Exception ex)
            {
                if (DeliveryServiceHelpers.IsFatalException(ex))
                {
                    throw;
                }

                TraceUtility.TraceCatchException(Tracing.Source, ex);
                s_perfCounterActive = false;
            }
        }

        public static void OverridesDBLatency(long ticks)
        {
            try
            {
                if (s_perfCounterActive)
                {
                    s_latencyOverridesDBAverageTimer.IncrementBy(ticks);
                    s_latencyOverridesDBAverageBase.Increment();

                    // Bump up the counter for all DB Requests
                    s_latencyRequestDBAverageTimer.IncrementBy(ticks);
                    s_latencyRequestDBAverageBase.Increment();
                    s_numberOfTotalDBRequestsPerSec.Increment();
                }
            }
            catch (Exception ex)
            {
                if (DeliveryServiceHelpers.IsFatalException(ex))
                {
                    throw;
                }

                TraceUtility.TraceCatchException(Tracing.Source, ex);
                s_perfCounterActive = false;
            }
        }

        #endregion

        #region Response Type Counters
        public static void KnownResponse(int matches)
        {
            try
            {
                if (s_perfCounterActive)
                {
                    s_numberOfTotalKnownResponses.IncrementBy(matches);
                }
            }
            catch (Exception ex)
            {
                if (DeliveryServiceHelpers.IsFatalException(ex))
                {
                    throw;
                }

                TraceUtility.TraceCatchException(Tracing.Source, ex);
                s_perfCounterActive = false;
            }
        }

        public static void UnknownResponse(int notFound)
        {
            try
            {
                if (s_perfCounterActive)
                {
                    s_numberOfTotalUnknownResponses.IncrementBy(notFound);
                }
            }
            catch (Exception ex)
            {
                if (DeliveryServiceHelpers.IsFatalException(ex))
                {
                    throw;
                }

                TraceUtility.TraceCatchException(Tracing.Source, ex);
                s_perfCounterActive = false;
            }
        }
        #endregion

        public static long NumberOfTotalRequests
        {
            get
            {
                return s_numberOfTotalRequests.RawValue;
            }
        }

        public static long NumberOfTotalLookupRequests
        {
            get
            {
                return s_numberOfTotalLookupRequests.RawValue;
            }
        }
    }
}
